package com.sc.nft.dao;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sc.nft.dto.MyWantBuyRecordVO;
import com.sc.nft.dto.MyWantSellRecordVO;
import com.sc.nft.entity.SecondEquityPropsOrder;
import com.sc.nft.entity.dto.RefundOrderPageDTO;
import com.sc.nft.entity.vo.AdminSecondEquityPropsOrderListVO;
import com.sc.nft.entity.vo.RefundOrderPageVO;
import com.sc.nft.entity.vo.SecondEquityPropTradingTendPageVO;
import com.sc.nft.entity.vo.secondEquityProp.SecondEquityPropBuyOrderPageVO;
import com.sc.nft.entity.vo.secondEquityProp.SecondEquityPropTradingTendDetailsVO;
import com.sc.nft.enums.OrderStatusEnum;
import com.sc.nft.enums.PayMethodEnum;
import com.sc.nft.enums.RedisKeyEnum;
import com.sc.nft.mapper.SecondEquityPropsOrderMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.sc.nft.dao.SecondOrderDao.secondsToTheNextMorning;

/**
 * 二级-权益道具订单表(sc_second_equity_props_order)数据DAO
 *
 * @author Sun
 * @description 由 Mybatisplus Code Generator 创建
 * @since 2024-04-07 14:18:41
 */
@Slf4j
@Repository
public class SecondEquityPropsOrderDao extends ServiceImpl<SecondEquityPropsOrderMapper, SecondEquityPropsOrder> {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public Page<AdminSecondEquityPropsOrderListVO> adminDealEquityPropsPage(Page page, String buyUserTel, String salesUserTel, String firstTitle, Date startTime, Date endTime, Date dealStartTime, Date dealEndTime) {
        return baseMapper.adminDealEquityPropsPage(page, buyUserTel, salesUserTel, firstTitle, startTime, endTime, dealStartTime, dealEndTime);
    }

    public List<SecondEquityPropsOrder> getWaitSettleOrders() {
        return lambdaQuery().eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.PAYMENT).eq(SecondEquityPropsOrder::getIsSettlePay, false).last(" limit 1000").list();
    }

    public boolean hasWaitPayOrder(Long userId) {
        return lambdaQuery().eq(SecondEquityPropsOrder::getBuyUserId, userId).eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.WAIT_PAY).count() > 0;
    }

    public List<SecondEquityPropsOrder> findWaitingPayByIds(List<Long> ids) {
        return lambdaQuery().eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.WAIT_PAY).in(SecondEquityPropsOrder::getId, ids).list();
    }

    public boolean updateStatusLockToSuccess(Long id, Date payTime, PayMethodEnum receiveType) {
        LambdaUpdateWrapper<SecondEquityPropsOrder> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(SecondEquityPropsOrder::getId, id);
        wrapper.eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.WAIT_PAY);
        wrapper.set(SecondEquityPropsOrder::getStatus, OrderStatusEnum.PAYMENT);
        wrapper.set(SecondEquityPropsOrder::getPayTime, payTime);
        wrapper.set(SecondEquityPropsOrder::getReceiveType, receiveType);
        return update(wrapper);
    }


    public boolean updateSettleFlag(Long id) {
        LambdaUpdateWrapper<SecondEquityPropsOrder> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(SecondEquityPropsOrder::getId, id);
        wrapper.eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.PAYMENT);
        wrapper.eq(SecondEquityPropsOrder::getIsSettlePay, false);
        wrapper.set(SecondEquityPropsOrder::getIsSettlePay, true);
        return update(wrapper);
    }

    public List<SecondEquityPropsOrder> getListByIdsAndUserIdAndEquityPropsId(List<Long> orderIds, Long buyUserId, Long equityPropsId) {
        LambdaQueryWrapper<SecondEquityPropsOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(SecondEquityPropsOrder::getId, orderIds);
        wrapper.eq(SecondEquityPropsOrder::getBuyUserId, buyUserId);
        wrapper.eq(SecondEquityPropsOrder::getEquityPropsId, equityPropsId);
        wrapper.eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.WAIT_PAY);
        return list(wrapper);
    }

    public Page<MyWantBuyRecordVO> getByWantBuyOrderId(Long id, Page page) {
        return baseMapper.getByWantBuyOrderId(page, id);
    }

    public Page<MyWantSellRecordVO> getByWantSellOrderId(Long id, Page page) {
        return baseMapper.getByWantSellOrderId(page, id);
    }

    public SecondEquityPropTradingTendDetailsVO tradingTendDetails(Long equityPropsId) {
        DateTime now = DateTime.now();
        DateTime startTime = DateUtil.beginOfDay(now);
        Date endTime = DateUtil.endOfDay(now);
        return baseMapper.tradingTendDetails(equityPropsId, startTime, endTime);
    }

    public Page<SecondEquityPropTradingTendPageVO> flowRecord(Page page, Long equityPropsId) {
        return baseMapper.flowRecord(page, equityPropsId);
    }

    public Page<SecondEquityPropBuyOrderPageVO> orderPage(Page page, Long userId, OrderStatusEnum status, DateTime beginTime, DateTime endTime) {
        return baseMapper.orderPage(page, userId, status.getCode(), beginTime, endTime);
    }

    public Page<SecondEquityPropBuyOrderPageVO> salesOrderPage(Page page, Long userId, DateTime beginTime, DateTime endTime) {
        return baseMapper.salesOrderPage(page, userId, beginTime, endTime);
    }

    public boolean updateStatusLockToCancel(Long id, OrderStatusEnum status) {
        LambdaUpdateWrapper<SecondEquityPropsOrder> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(SecondEquityPropsOrder::getId, id);
        wrapper.eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.WAIT_PAY);
        wrapper.set(SecondEquityPropsOrder::getStatus, status);
        wrapper.set(SecondEquityPropsOrder::getRealCloseTime, DateTime.now());
        return update(wrapper);
    }

    public List<SecondEquityPropsOrder> getPreTimeOutOrderIds(DateTime offset) {
        return lambdaQuery().eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.WAIT_PAY).lt(SecondEquityPropsOrder::getCreateTime, offset).list();
    }

    public SecondEquityPropsOrder selectByRelationIdSubOneDayData(Long id) {
        String key = StrUtil.format("{}:{}", RedisKeyEnum.EQUITY_PROP_LEVEL2_ORDERS_GET_THE_LAST_DATA_OF_YESTERDAY, id);
        // 判定对应的KEY是否存在,存在直接反序列化返回.不存在就查询出来写入到缓存在返回.
        Boolean exist = stringRedisTemplate.hasKey(key);
        if (exist) {
            return JSON.parseObject(stringRedisTemplate.opsForValue().get(key), SecondEquityPropsOrder.class);
        }
        SecondEquityPropsOrder secondEquityPropsOrder = baseMapper.selectByRelationIdSubOneDayData(id);
        // 把数据序列化以后写入缓存中,保存到第二天凌晨12点过期
        // 计算当前时间,到第二天凌晨的时间差,单位为秒
        stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(secondEquityPropsOrder), secondsToTheNextMorning(), TimeUnit.SECONDS);
        return secondEquityPropsOrder;


    }

    public List<SecondEquityPropsOrder> getLockOrderByUserId(Long userId) {
        LambdaQueryWrapper<SecondEquityPropsOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SecondEquityPropsOrder::getBuyUserId, userId);
        wrapper.eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.WAIT_PAY);
        return list(wrapper);
    }

    public SecondEquityPropsOrder getOneByIdAndUserId(Long orderId, Long userId) {
        LambdaQueryWrapper<SecondEquityPropsOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SecondEquityPropsOrder::getId, orderId);
        wrapper.eq(SecondEquityPropsOrder::getBuyUserId, userId);
        return getOne(wrapper);
    }

    public Boolean getIsLockBysecondProductid(Long secondProductid) {
        LambdaQueryWrapper<SecondEquityPropsOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SecondEquityPropsOrder::getSecondEquityPropsId, secondProductid);
        wrapper.in(SecondEquityPropsOrder::getStatus, OrderStatusEnum.WAIT_PAY, OrderStatusEnum.PAYMENT);
        return count(wrapper) > 0;
    }

    public IPage<RefundOrderPageVO> refundOrderPage(RefundOrderPageDTO request, Long userId) {
        LambdaQueryWrapper<SecondEquityPropsOrder> wrapper = Wrappers.lambdaQuery();
        wrapper.select(SecondEquityPropsOrder::getOrderNo, SecondEquityPropsOrder::getFirstTitle, SecondEquityPropsOrder::getBuyPrice, SecondEquityPropsOrder::getStatus, SecondEquityPropsOrder::getCreateTime, SecondEquityPropsOrder::getPayTime);
        wrapper.eq(SecondEquityPropsOrder::getBuyUserId, userId);
        wrapper.eq(request.getStatus() != null && request.getStatus() != OrderStatusEnum.ALL, SecondEquityPropsOrder::getStatus, request.getStatus());
        wrapper.eq(StrUtil.isNotEmpty(request.getOrderNo()), SecondEquityPropsOrder::getOrderNo, request.getOrderNo());
        wrapper.orderByDesc(SecondEquityPropsOrder::getId);
        Page page = page(request.toMp(), wrapper);
        List<SecondEquityPropsOrder> records = page.getRecords();
        List<RefundOrderPageVO> refundOrderPageVOS = BeanUtil.copyToList(records, RefundOrderPageVO.class);
        page.setRecords(refundOrderPageVOS);
        return page;
    }

    public SecondEquityPropBuyOrderPageVO buyOrderDetail(Long userId, Long orderId) {
        return baseMapper.buyOrderDetail(userId, orderId);
    }

    public SecondEquityPropBuyOrderPageVO salesOrderDetail(Long userId, Long orderId) {
        return baseMapper.salesOrderDetail(userId, orderId);
    }

    public BigDecimal getLastPrice(Long equityPropsId) {
        LambdaQueryWrapper<SecondEquityPropsOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SecondEquityPropsOrder::getEquityPropsId, equityPropsId);
        wrapper.eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.PAYMENT);
        wrapper.orderByDesc(SecondEquityPropsOrder::getCreateTime);
        wrapper.last("limit 1");
        SecondEquityPropsOrder secondEquityPropsOrder = getOne(wrapper);
        return Objects.nonNull(secondEquityPropsOrder) ? secondEquityPropsOrder.getBuyPrice() : BigDecimal.ZERO;
    }

    public IPage<SecondEquityPropsOrder> wantBuyOrderPage(Page page, Long wantBuyOrderId) {
        LambdaQueryWrapper<SecondEquityPropsOrder> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(SecondEquityPropsOrder::getIsSecondWantBuyOrder, Boolean.TRUE);
        wrapper.eq(SecondEquityPropsOrder::getSecondWantBuyOrderId, wantBuyOrderId);
        wrapper.eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.PAYMENT);
        wrapper.orderByDesc(SecondEquityPropsOrder::getCreateTime);
        return baseMapper.selectPage(page, wrapper);
    }

    public IPage<SecondEquityPropsOrder> wantBuyOrderPage(Page page, Long wantBuyOrderId, Long salesUserId, Date startCreateTime, Date endCreateTime, Date startPayTime, Date endPayTime) {
        LambdaQueryWrapper<SecondEquityPropsOrder> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(SecondEquityPropsOrder::getIsSecondWantBuyOrder, Boolean.TRUE);
        wrapper.eq(SecondEquityPropsOrder::getSecondWantBuyOrderId, wantBuyOrderId);
        wrapper.eq(Objects.nonNull(salesUserId), SecondEquityPropsOrder::getSalesUserId, salesUserId);
        wrapper.ge(Objects.nonNull(startCreateTime), SecondEquityPropsOrder::getCreateTime, startCreateTime);
        wrapper.le(Objects.nonNull(endCreateTime), SecondEquityPropsOrder::getCreateTime, endCreateTime);
        wrapper.ge(Objects.nonNull(startPayTime), SecondEquityPropsOrder::getPayTime, startPayTime);
        wrapper.le(Objects.nonNull(endPayTime), SecondEquityPropsOrder::getPayTime, endPayTime);
        wrapper.eq(SecondEquityPropsOrder::getStatus, OrderStatusEnum.PAYMENT);
        wrapper.orderByDesc(SecondEquityPropsOrder::getCreateTime);
        return baseMapper.selectPage(page, wrapper);
    }

}